using System;
using System.Collections;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using igp.net.DataModel;

namespace igp.net.DataModel
{
    internal class igpPointComparer : IComparer
    {
        #region IComparer Members

        int IComparer.Compare(object x, object y)
        {
            return ((IgpPoint) x).X < ((IgpPoint) y).X ? -1 : 1;
        }

        #endregion
    }

    /// <summary>
    /// Represents a single point. These objects are formed into lists inside a PointSet.
    /// </summary>
    public class IgpPoint : IgpItem
    {
        private double Xo, Yo;

        /// <summary>
        /// Default constructor.
        /// </summary>
        public IgpPoint()
        {
            X = 0;
            Y = 0;
            LineColor = Color.Red;
            IsSelected = false;
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="x">X coordinate</param>
        /// <param name="y">Y coordinate</param>
        public IgpPoint(double x, double y)
        {
            X = x;
            Y = y;
            LineColor = Color.Red;
            IsSelected = false;
        }

        /// <summary>
        /// X coordinate of the point.
        /// </summary>
        public double X { get; set; }

        /// <summary>
        /// Y ccorinate of the point.
        /// </summary>
        public double Y { get; set; }

        /// <summary>
        /// Represents a PointSet that contains this point.
        /// </summary>
        public igpPointSet PointSet { get; internal set; }

        /// <summary>
        /// Draws the point on the screen.
        /// </summary>
        /// <param name="graph">Graphics object used for drawing.</param>
        public override void Draw(Graphics graph)
        {
            int Size = PointSet.PointSize;
            IgpPointType Type = PointSet.PointType;
            IgpCoordinates PointCoordinates = PointSet.PointCoordinates;
            Color FrameColor = PointSet.FrameColor, FillColor = PointSet.FillColor;

            int x = IgpProject.Singleton.View.GetScreenCoordinateX(X);
            int y = IgpProject.Singleton.View.GetScreenCoordinateY(Y);
            var rect = new Rectangle(x - Size/2, y - Size/2, Size, Size);

            Color fc;
            Color cc;
            if (!IsSelected)
            {
                fc = FillColor;
                cc = FrameColor;
            }
            else
            {
                fc = Color.Red;
                cc = Color.Red;
            }
            //Square
            if (Type == IgpPointType.Square)
            {
                graph.FillRectangle(new SolidBrush(fc), rect);
                graph.DrawRectangle(new Pen(FrameColor), rect);
            }
            //Circle
            if (Type == IgpPointType.Circle)
            {
                graph.FillEllipse(new SolidBrush(fc), rect);
                graph.DrawEllipse(new Pen(FrameColor), rect);
            }

            //Cross
            if (Type == IgpPointType.Cross)
            {
                graph.DrawLine(new Pen(cc, 2), x - Size/2, y - Size/2, x + Size/2, y + Size/2);
                graph.DrawLine(new Pen(cc, 2), x - Size/2, y + Size/2, x + Size/2, y - Size/2);
            }

            //Triangle
            if (Type == IgpPointType.Triangle)
            {
                var pts = new Point[3];
                pts[0].X = x - Size/2;
                pts[0].Y = y + Size/2;
                pts[1].X = x + Size/2;
                pts[1].Y = y + Size/2;
                pts[2].X = x;
                pts[2].Y = y - Size/2;

                graph.FillPolygon(new SolidBrush(fc), pts);
                graph.DrawPolygon(new Pen(FrameColor), pts);
            }

            //Coordinates
            var tp = new Point();
            if (PointCoordinates == IgpCoordinates.none) return;
            if (PointCoordinates == IgpCoordinates.above)
                tp = new Point(x - GetStringSize(graph).Width/2, y - Size - 10);
            if (PointCoordinates == IgpCoordinates.below) tp = new Point(x - GetStringSize(graph).Width/2, y + Size);
            if (PointCoordinates == IgpCoordinates.left)
                tp = new Point(x - GetStringSize(graph).Width - 5, y - GetStringSize(graph).Height/2);
            if (PointCoordinates == IgpCoordinates.right) tp = new Point(x + 5, y - GetStringSize(graph).Height/2);

            DrawCoordinates(graph, tp, false);
        }

        /// <summary>
        /// This method is required for item's reaction to mouse buttons being pressed. 
        /// You must invoke this base method in your derived class in order 
        /// to update some useful properties.
        /// </summary>
        /// <param name="graph">Graphics object that can be used to paint something on the screen</param>
        /// <param name="e">Mouse Event Details</param>
        public override void InteractDown(Graphics graph, MouseEventArgs e)
        {
            base.InteractDown(graph, e);

            if (IsSelected)
            {
                Xo = X;
                Yo = Y;
            }

            if (InteractionInUse) return;

            if (PointSet.EditMode == IgpPointEditMode.Edit)
                if (GetRect().Contains(MouseWhenPressed.X, MouseWhenPressed.Y))
                {
                    if (PointSet.SelectedCount() == 0) IsSelected = true;
                    InteractionInUse = true;
                    PointSet.CurrentMode = PointSet.EditMode;
                    Xo = X;
                    Yo = Y;
                }

            if (PointSet.EditMode == IgpPointEditMode.Select)
                if (GetRect().Contains(MouseWhenPressed.X, MouseWhenPressed.Y))
                {
                    PointSet.CurrentMode = IgpPointEditMode.Select;
                    IsSelected = !IsSelected;
                    Xo = X;
                    Yo = Y;
                }
        }

        private Rectangle GetRect()
        {
            int Size = PointSet.PointSize;
            IgpView view = IgpProject.Singleton.View;
            int x = view.GetScreenCoordinateX(X);
            int y = view.GetScreenCoordinateY(Y);
            double ratiox = view.GetRatioX();
            double ratioy = view.GetRatioY();

            return new Rectangle(
                (int) (x*ratiox - Math.Max(Size/2, 5)),
                (int) (y*ratioy - Math.Max(Size/2, 5)),
                (Math.Max(Size, 10)),
                (Math.Max(Size, 10)));
        }

        /// <summary>
        /// This method is required for item's reaction to mouse movement. 
        /// You must invoke this base method in your derived class in order 
        /// to update some useful properties.
        /// </summary>
        /// <param name="graph">Graphics object that can be used to paint something on the screen</param>
        /// <param name="e">Mouse Event Details</param>
        public override void InteractMove(Graphics graph, MouseEventArgs e)
        {
            base.InteractMove(graph, e);

            int Size = PointSet.PointSize;
            IgpCoordinates PointCoordinates = PointSet.PointCoordinates;

            IgpView view = IgpProject.Singleton.View;
            int x = view.GetScreenCoordinateX(X);
            int y = view.GetScreenCoordinateY(Y);
            var rect = new Rectangle(x - Size/2 - 5, y - Size/2 - 5, Size + 10, Size + 10);

            if (PointSet.CurrentMode == IgpPointEditMode.Edit && IsSelected)
            {
                if (e.Button == MouseButtons.Left)
                {
                    double DoubleVectorX = !view.LogScaleX ? VectorX/view.VDistanceX : VectorX/view.VLogDistanceX;
                    double DoubleVectorY = !view.LogScaleY ? VectorY/view.VDistanceY : VectorY/view.VLogDistanceY;

                    X = !view.LogScaleX ? Xo - DoubleVectorX : Math.Pow(10, (Math.Log10(Xo) - DoubleVectorX/10));

                    if (PointSet.Driver == null)
                    {
                        Y = !view.LogScaleY ? Yo + DoubleVectorY : Math.Pow(10, (Math.Log10(Yo) + DoubleVectorY/10));
                    }
                    else
                    {
                        Y = PointSet.Driver.Value(X);
                    }

                    if (PointCoordinates == IgpCoordinates.none)
                    {
                        var tp = new Point(Math.Min(Math.Max(x - GetStringSize(graph).Width/2, 25), view.Width - GetStringSize(graph).Width - 5),
                                           Math.Min(Math.Max(y - Size - 20, 5), view.Height - 25));
                        DrawCoordinates(graph, tp, true);
                    }

                    IgpProject.Singleton.UpdatePolynomials();
                }
            }

            if (GetRect().Contains(MouseWhenMoving.X, MouseWhenMoving.Y) && PointSet.CurrentMode == IgpPointEditMode.None)
            {
                graph.DrawRectangle(new Pen(Color.Red, 2), rect);

                //Coordinates
                if (PointCoordinates == IgpCoordinates.none)
                {
                    var tp = new Point(Math.Min(Math.Max(x - GetStringSize(graph).Width/2, 25), view.Width - GetStringSize(graph).Width - 5),
                                       Math.Min(Math.Max(y - Size - 20, 5), view.Height - 25));
                    DrawCoordinates(graph, tp, true);
                }

                FormMain.Singleton.EditorWindow.SetCurrentPointOnList(PointSet.Points.IndexOf(this));
            }
        }

        private Size GetStringSize(Graphics graph)
        {
            string st = PointSet.Filter.Replace("x", X.ToString("0.0000")).Replace("y", Y.ToString("0.0000"));
            return graph.MeasureString(st, igpSettings.Singleton.ChartFont).ToSize();
        }

        private void DrawCoordinates(Graphics graph, Point tp, bool DrawBackground)
        {
            string st = PointSet.Filter.Replace("x", X.ToString("0.0000")).Replace("y", Y.ToString("0.0000"));

            if (DrawBackground)
            {
                var re = new Rectangle(tp, graph.MeasureString(st, igpSettings.Singleton.ChartFont).ToSize());
                var re2 = new Rectangle(new Point(tp.X + 3, tp.Y + 3),
                                        graph.MeasureString(st, igpSettings.Singleton.ChartFont).ToSize());

                graph.FillRectangle(new SolidBrush(Color.FromArgb(100, Color.Black)), re2);
                graph.FillRectangle(new LinearGradientBrush(re, Color.White, Color.FromArgb(230, 230, 230), 0, false), re);
                graph.DrawRectangle(new Pen(Color.DarkGray), re);
            }
            graph.DrawString(st, igpSettings.Singleton.ChartFont, new SolidBrush(Color.Black), tp);
        }

        /// <summary>
        /// This method is required for item's reaction to mouse buttons being released. 
        /// </summary>
        /// <param name="graph">Graphics object that can be used to paint something on the screen</param>
        /// <param name="e">Mouse Event Details</param>
        public override void InteractUp(Graphics graph, MouseEventArgs e)
        {
            base.InteractUp(graph, e);

            if (PointSet.EditMode == IgpPointEditMode.Edit && PointSet.SelectedCount() == 1)
            {
                IsSelected = false;
            }

            InteractionInUse = false;
            PointSet.CurrentMode = IgpPointEditMode.None;
        }
    }
}